package com.fuershiye.woman.monitor.dao;

import java.io.Serializable;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang.StringUtils;
import org.hibernate.Criteria;
import org.hibernate.Query;
import org.hibernate.SQLQuery;
import org.hibernate.Session;
import org.hibernate.criterion.Example;
import org.hibernate.criterion.MatchMode;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Projections;
import org.hibernate.criterion.Restrictions;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;

import com.fuershiye.woman.monitor.annotations.QueryEntry;
import com.fuershiye.woman.monitor.annotations.QueryField;
import com.fuershiye.woman.monitor.beans.Pagination;
import com.fuershiye.woman.monitor.beans.QueryFiledEntry;
import com.fuershiye.woman.monitor.utils.HqlHandler;

public class SpringHibernateDaoImpl extends HibernateDaoSupport implements HibernateDao {

  @SuppressWarnings("deprecation")
private Criteria createCriteria(String className) {
    Session session = getSession();
    return session.createCriteria(className).setCacheable(true);
  }

   @SuppressWarnings("deprecation")
public Session getCurrentSession(){
       return getSession();
   }

  @SuppressWarnings("unchecked")
  public Pagination queryByExample(Object instance, int startPage, int limit, String[][] orders) {
    int startRow = (startPage - 1) * limit;
    Criteria c = createCriteria(instance.getClass().getName());
    c = setOrder(c, orders).setFirstResult(startRow).setMaxResults(limit);
    c = createExampleCriteria(c, instance);

    Criteria cc = createCriteria(instance.getClass().getName()).setProjection(
            Projections.rowCount());
    cc = createExampleCriteria(cc, instance);
    Integer count = ((Long) cc.list().get(0)).intValue();
    int pageSize = count.intValue() % limit == 0 ? count.intValue() / limit : count.intValue()
            / limit + 1;
    return this.initPagination(limit, startPage, pageSize, count, c.list());
  }

  public List<?> queryByExample(Object instance, int max, String[][] orders) {
    Criteria c = createCriteria(instance.getClass().getName()).setMaxResults(max);
    c = setOrder(c, orders);
    c = createExampleCriteria(c, instance);
    return c.list();
  }

  public List<?> queryByExample(Object instance, int max) {
    Criteria c = createCriteria(instance.getClass().getName()).setMaxResults(max);
    c = createExampleCriteria(c, instance);
    return c.list();
  }

  public List<?> queryByExample(Object instance, String[][] orders) {
    Criteria c = createCriteria(instance.getClass().getName());
    c = setOrder(c, orders);
    c = createDetailedCriteria(c, instance);
    return c.list();
  }

  public List<?> queryByExample(Object instance) {
    Criteria c = createCriteria(instance.getClass().getName());
    c = createExampleCriteria(c, instance);
    return c.list();
  }

  public List<?> queryByDetailed(Object instance, int first, int max, String[][] orders) {
    Criteria c = createCriteria(instance.getClass().getName());
    c = setOrder(c, orders).setFirstResult(first).setMaxResults(max);
    c = createDetailedCriteria(c, instance);
    return c.list();
  }

  public List<?> queryByDetailed(Object instance, int first, int max) {
    Criteria c = createCriteria(instance.getClass().getName()).setFirstResult(first).setMaxResults(
            max);
    c = createDetailedCriteria(c, instance);
    return c.list();
  }

  public List<?> queryByDetailed(Object instance, int max) {
    Criteria c = createCriteria(instance.getClass().getName());
    c = createDetailedCriteria(c, instance).setMaxResults(max);
    return c.list();
  }

	public List<?> queryByDetailed(Object instance, String[][] orders) {
		Criteria c = createCriteria(instance.getClass().getName());
		c = setOrder(c, orders);
		c = createDetailedCriteria(c, instance);
		return c.list();
	}

  public List<?> queryByDetailed(Object instance) {
    Criteria c = createCriteria(instance.getClass().getName());
    c = createDetailedCriteria(c, instance);
    return c.list();
  }

  @SuppressWarnings("unchecked")
  public Pagination queryByDetailedAsPagination(Object instance, int startPage, int limit,
          String[][] orders) {
    int startRow = (startPage - 1) * limit;
    Criteria c = createCriteria(instance.getClass().getName());
    c = setOrder(c, orders);
    c.setFirstResult(startRow).setMaxResults(limit);
    c = createDetailedCriteria(c, instance);

    Criteria cc = createCriteria(instance.getClass().getName()).setProjection(
            Projections.rowCount());
    cc = createDetailedCriteria(cc, instance);
    Integer count = Integer.parseInt(cc.list().get(0).toString());
    int pageSize = count.intValue() % limit == 0 ? count.intValue() / limit : count.intValue()
            / limit + 1;
    return this.initPagination(limit, startPage, pageSize, count, c.list());
  }

  private Criteria createExampleCriteria(Criteria c, Object instance) {
    c = c.add(Example.create(instance));
    Class<?> cls = instance.getClass();
    Method[] methods = cls.getMethods();
    for (int i = 0; i < methods.length; i++) {
      Method m = methods[i];
      if (m.isAnnotationPresent(QueryEntry.class)) {
        QueryEntry qe = m.getAnnotation(QueryEntry.class);
        try {
          Object value = m.invoke(instance);
          if (value != null) {
            Criteria rc = c.createCriteria(qe.name()).add(Example.create(value));
            createExampleCriteria(rc, value);
          }
        } catch (IllegalArgumentException e) {
          e.printStackTrace();
        } catch (IllegalAccessException e) {
          e.printStackTrace();
        } catch (InvocationTargetException e) {
          e.printStackTrace();
        }
      }
    }
    return c;
  }

  private Criteria createDetailedCriteria(Criteria c, Object likeInstance) {
    Class<?> cls = likeInstance.getClass();
    Method[] methods = cls.getMethods();
    for (int i = 0; i < methods.length; i++) {
      Method m = methods[i];
      try {

        if (m.isAnnotationPresent(QueryEntry.class)) {
          Object value = m.invoke(likeInstance);
          if (value != null) {
            QueryEntry qe = m.getAnnotation(QueryEntry.class);
            Criteria rc = c.createCriteria(qe.name());
            createDetailedCriteria(rc, value);
          }
        } else if (m.isAnnotationPresent(QueryField.class)) {
          Object value = m.invoke(likeInstance);
          if (value != null && !StringUtils.isEmpty(String.valueOf(value))
                  && !String.valueOf(value).equals("0.0")) {
            QueryField qf = m.getAnnotation(QueryField.class);
            if (qf.exp() == QueryField.QUERY_EXP_EQ) {
              c = c.add(Restrictions.eq(qf.name(), value));
            } else if (qf.exp() == QueryField.QUERY_EXP_ALLEQ) {
              c = c.add(Restrictions.allEq((Map<?, ?>) value));
            } else if (qf.exp() == QueryField.QUERY_EXP_LIKE) {
              c = c.add(Restrictions.like(qf.name(), value));
            } else if (qf.exp() == QueryField.QUERY_EXP_LIKE_ANYWHERE) {
              c = c.add(Restrictions.like(qf.name(), value.toString(), MatchMode.ANYWHERE));
            } else if (qf.exp() == QueryField.QUERY_EXP_LIKE_START) {
              c = c.add(Restrictions.like(qf.name(), value.toString(), MatchMode.START));
            } else if (qf.exp() == QueryField.QUERY_EXP_LIKE_END) {
              c = c.add(Restrictions.like(qf.name(), value.toString(), MatchMode.END));
            } else if (qf.exp() == QueryField.QUERY_EXP_BETWEEN) {
              Object[] objs = (Object[]) value;
              c = c.add(Restrictions.between(qf.name(), objs[0], objs[1]));
            } else if (qf.exp() == QueryField.QUERY_EXP_IN) {
              Object[] objs = (Object[]) value;
              if (objs.length > 0) {
                c = c.add(Restrictions.in(qf.name(), objs));
              }
            } else if (qf.exp() == QueryField.QUERY_EXP_LE) {
              c = c.add(Restrictions.le(qf.name(), value));
            } else if (qf.exp() == QueryField.QUERY_EXP_LT) {
              c = c.add(Restrictions.lt(qf.name(), value));
            } else if (qf.exp() == QueryField.QUERY_EXP_GE) {
              c = c.add(Restrictions.ge(qf.name(), value));
            } else if (qf.exp() == QueryField.QUERY_EXP_GT) {
              c = c.add(Restrictions.gt(qf.name(), value));
            } else if (qf.exp() == QueryField.QUERY_EXP_SQL) {
              c = c.add(Restrictions.sqlRestriction("(" + String.valueOf(value) + ")"));
            } else if (qf.exp() == QueryField.QUERY_EXP_NE) {
              c = c.add(Restrictions.ne(qf.name(), value));  
            }
          }
        }
      } catch (IllegalArgumentException e) {
        e.printStackTrace();
      } catch (IllegalAccessException e) {
        e.printStackTrace();
      } catch (InvocationTargetException e) {
        e.printStackTrace();
      }
    }

    return c;
  }

  private Criteria setOrder(Criteria c, String[][] orders) {
    if (orders != null) {
      for (int i = 0; i < orders.length; i++) {
        if ("asc".equalsIgnoreCase(orders[i][1])) {
          c = c.addOrder(Order.asc(orders[i][0]));
        } else if ("desc".equalsIgnoreCase(orders[i][1])) {
          c = c.addOrder(Order.desc(orders[i][0]));
        }
      }
    }
    return c;
  }

  @SuppressWarnings("deprecation")
public Query getQuery(String hql) {
    return getSession().createQuery(hql).setCacheable(true);
  }

  @SuppressWarnings("unchecked")
public <T> T getById(Class<T> entryClass, Object id) {
    T instance = (T) getHibernateTemplate().get(entryClass.getName(), (Serializable) id);
    return instance;
  }

  @SuppressWarnings({ "deprecation", "unchecked" })
public <T> List<T> getAll(Class<T> entryClass) {
    Criteria criteria = getSession().createCriteria(entryClass);
    return criteria.list();
  }

  @SuppressWarnings("unchecked")
public <T> T loadById(Class<T> entryClass, Object id) {
    T instance = (T) getHibernateTemplate().load(entryClass.getName(), (Serializable) id);
    return instance;
  }

  @SuppressWarnings("deprecation")
public void save(Object persistentInstance) {
    getHibernateTemplate().saveOrUpdate(persistentInstance);
    this.getSession().beginTransaction().commit();
  }

  public void saveNotCommit(Object persistentInstance) {
    getHibernateTemplate().saveOrUpdate(persistentInstance);
  }

  public void commit() {
    // this.getSession().beginTransaction().commit();
  }

  public void delete(Object persistentInstance) {
    getHibernateTemplate().delete(persistentInstance);
  }

  @SuppressWarnings("deprecation")
public void flush() {
    this.getSession().flush();

  }

  @SuppressWarnings("deprecation")
public void close() {
    this.getSession().close();
  }

  @SuppressWarnings("deprecation")
public void clear() {
    this.getSession().clear();
  }

  @SuppressWarnings("deprecation")
public void evict(Object obj) {
    this.getSession().evict(obj);
  }

  @SuppressWarnings("deprecation")
public Object merge(Object object) {
    return this.getSession().merge(object);
  }

  public Pagination resetPagination(Pagination pagination, int count) {
    int rsCount = pagination.getRowCount();
    rsCount += count;
    int limit = pagination.getLimit();
    int pageSize = rsCount % limit == 0 ? rsCount / limit : rsCount / limit + 1;
    pagination.setRowCount(rsCount);
    pagination.setPageSize(pageSize);
    return pagination;
  }

  @SuppressWarnings("unchecked")
  public Pagination getPagination(Query query, Query countQuery, int startPage, int limit) {
    int startRow = (startPage - 1) * limit;
    List<Object> rsList = query.setFirstResult(startRow).setMaxResults(limit).list();
    Long num = (Long) countQuery.uniqueResult();
    int count = (num == null ? 0 : num.intValue());
    int pageSize = count % limit == 0 ? count / limit : count / limit + 1;
    return this.initPagination(limit, startPage, pageSize, count, rsList);
  }

  private Pagination initPagination(int limit, int startPage, int pageSize, int count,
          List<Object> resultList) {
    Pagination pn = new Pagination();
    pn.setLimit(limit);
    pn.setPageNum(startPage);
    pn.setPageSize(pageSize);
    pn.setResultList(resultList);
    pn.setRowCount(count);
    return pn;
  }

  public List<?> queryByDetailedAndEntrys(Object instance, List<QueryFiledEntry> queryfiledentrys) {
    String hql = HqlHandler.createHql(instance, queryfiledentrys);
    return this.getQuery(hql).list();
  }

  public List<?> queryByDetailedAndEntrys(Object instance, List<QueryFiledEntry> queryfiledentrys,
          int max, String[][] orders) {
    String hql = HqlHandler.createHql(instance, queryfiledentrys, orders);
    return this.getQuery(hql).setMaxResults(max).list();
  }

  public List<?> queryByDetailedAndEntrys(Object instance, List<QueryFiledEntry> queryfiledentrys,
          int first, int max, String[][] orders) {
    String hql = HqlHandler.createHql(instance, queryfiledentrys, orders);
    return this.getQuery(hql).setFirstResult(first).setMaxResults(max).list();
  }

  public List<?> queryByDetailedGroup(Object instance, String selectSub, String[] groupField,
          String[][] orders, Integer limit) {
    String hql = HqlHandler.createGroupHql(instance, selectSub, groupField, 1, orders);
    Query query = this.getQuery(hql);
    if (limit != null && limit > 0) {
      query.setMaxResults(limit);
    }
    return query.list();
  }

  public List<?> queryByDetailedGroup(Object instance, String selectSub, String[] groupField,
          String[][] orders) {
    return queryByDetailedGroup(instance, selectSub, groupField, orders, null);
  }

  public Pagination queryByDetailedAndEntrysAsPagination(Object instance,
          List<QueryFiledEntry> queryfiledentrys, int startPage, int limit, String[][] orders) {
    String hql = HqlHandler.createHql(instance, queryfiledentrys, orders);
    Query query = this.getQuery(hql);
    String countHql = HqlHandler.createCountHql(instance, queryfiledentrys, orders);
    Query countQuery = this.getQuery(countHql);
    return this.getPagination(query, countQuery, startPage, limit);
  }

  public List<?> queryAllOrder() {

    return null;
  }

  @SuppressWarnings("deprecation")
public void updateMessage(Object persistentInstance) {
    getHibernateTemplate().update(persistentInstance);
    this.getSession().beginTransaction().commit();
  }

  @SuppressWarnings("deprecation")
public SQLQuery getSqlQuery(String sql) {
    return getSession().createSQLQuery(sql);
  }

}
